package cn.mw.cmdb.entity;

import cn.mw.cmdb.enums.RelatedSelectEnum;
import cn.mw.cmdb.param.ImportContextParam;
import cn.mw.components.mongodb.entity.CriteriaOpsType;
import cn.mw.components.mongodb.entity.MongoPropertyType;
import cn.mw.components.mongodb.entity.SubCondition;
import cn.mw.components.mongodb.exception.TypeQueryNotSupportException;
import cn.mw.microMonitorCommon.api.user.MwUserApi;
import cn.mw.microMonitorCommon.entity.cmdb.ModelExportDataInfoParam;
import cn.mw.microMonitorCommon.entity.cmdb.MwImportResultParam;
import com.google.common.base.Strings;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static cn.mw.cmdb.entity.InstanceInfo.SearchPrefix;
import static cn.mw.cmdb.param.MwRelationPropertyType.relationPropertyIdKey;
import static cn.mw.cmdb.service.impl.InstanceViewImpl.nameKey;
import static cn.mw.cmdb.util.ValConvertUtil.booleanValueConvert;
import static cn.mw.components.mongodb.entity.CriteriaOpsType.like;

@Data
public abstract class PropertyType<T, C> extends MongoPropertyType<T, C> {

    public static final String relationIdKey = "relationId";
    public static final String relationPath = "relationPath";
    public static final String modifierPath = "modifier";
    public static final String createrPath = "creater";

    public static final String relationNameKey = "relationName";
    public static final String matchRuleKey = "matchRule";
    public static final String lengthNumKey = "lengthNum";
    public static final String maxNumKey = "maxNum";
    public static final String minNumKey = "minNum";
    public static final String numericalTypeKey = "numericalType";
    public static final String integerTypeKey = "integerType";
    public static final String decimalLengthKey = "decimalLength";
    public static final String roundingModeKey = "roundingMode";
    public static final String dateCompareKey = "dateCompareType";
    public static final String compareRuleKey = "compareRule";
    public static final String compareValueKey = "compareValue";
    //默认的字符串最大长度
    public static final int defaultStrMaxlength = 256;
    public static final String patternKey = "pattern";
    public static final String prefixStrKey = ":[";
    public static final String defaultPrefixStrKey = ":默认值[";
    public static final String fixedValStrKey = "固定值";
    public static final String currentTimeStrKey = "当前系统时间";


    private PropertyTypeManage propertyTypeManage;

    private ApplicationContext applicationContext;
    public static MwUserApi userApi;
    ;


    public void init(MwUserApi userApi) {
        this.userApi = userApi;
    }

    public void init(ApplicationContext applicationContext, PropertyTypeManage propertyTypeManage) {
    }

    public boolean validateValue(Object value, PropertyTypeRef propertyTypeRef) throws Exception {
        return true;
    }

    public Criteria criteria(PropertyInfo propertyInfo, Map<String, SubCondition> params, String prefix) {
        SubCondition subCondition = params.get(propertyInfo.getName()) != null ? params.get(propertyInfo.getName()) : params.get(propertyInfo.getId());
        Criteria criteria = null;

        if (null != subCondition) {
            String propertyName = params.get(propertyInfo.getName()) != null ? propertyInfo.getName() : propertyInfo.getId();
            String name;
            if (StringUtils.hasText(RelatedSelectEnum.getRelatedFieldIdByName(propertyName))) {
                name = RelatedSelectEnum.getRelatedFieldIdByName(propertyInfo.getId());
            } else {
                name = !StringUtils.hasText(prefix) ? propertyName : formatSearchKey(prefix, propertyName);
            }
            String operation = subCondition.getOperation();
            Object value = subCondition.getValue();
            if (Strings.isNullOrEmpty(strValueConvert(value)) && like.name().equals(operation)) {
                return criteria;
            }
            if (value == null && !"ne".equals(operation)) {
                return criteria;
            }
            //设置不通属性类型的默认比较操作符
            CriteriaOpsType opsType = getDefaultCriteriaOpsType();
            if (StringUtils.hasText(subCondition.getOperation())) {
                opsType = CriteriaOpsType.valueOf(subCondition.getOperation());
            }

            if (StringUtils.hasText(propertyInfo.getSearchPrefix())) {
                name = propertyInfo.getSearchPrefix() + name;
            }
            criteria = doCriteria(opsType, name, value);
            if (null == criteria) {
                throw new TypeQueryNotSupportException(new Object[]{name});
            }
        }

        return criteria;
    }

    public Object listInputOption(PropertyTypeRef propertyTypeRef) throws Exception {
        return null;
    }

    @Override
    public abstract Criteria doCriteria(CriteriaOpsType opsType, String name, Object value);

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PropertyType<?, ?> that = (PropertyType<?, ?>) o;
        return Objects.equals(id, that.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    public Object getDataValue(ImportContextParam contextParam) throws Exception {
        MwImportResultParam resultParam = new MwImportResultParam();
        if (contextParam != null && contextParam.getParam() != null) {
            ModelExportDataInfoParam importParam = contextParam.getParam();
            //属性id
            Object cellValue = importParam.getCellValue();
            resultParam.setMessage("");
            resultParam.setCellValue(cellValue);
        }
        return resultParam;
    }

    public Object tableHeaderTipInfo(PropertyTypeRef propertyTypeRef) {
        return "";
    }

    public boolean tableHeaderInfo(Map ms, PropertyInfo propertyInfo, List<String> labelNames,List<String> label) throws Exception {
        //导出表头数据时，表头字段的id和名称不能同时重复
        if ((label != null && !label.contains(propertyInfo.getId())) && (labelNames != null && !labelNames.contains(ms.get(nameKey)))) {
            label.add(propertyInfo.getId());
            return true;
        }
        return false;
    }

    public static String getRelationIdPath(String name) {
        String path = SearchPrefix + "." + name + "." + relationIdKey;
        return path;
    }

    public static String getRelationPropertyIdPath(String name) {
        String path = SearchPrefix + "." + name + "." + relationPropertyIdKey;
        return path;
    }

    public static String getRelationNamePath(String name, String propertyTypeId) {
        String path = "";
        String queryName = "";
        if (name != null && name.indexOf(SearchPrefix) != -1) {
            queryName = name;
        } else {
            queryName = SearchPrefix + "." + name;
        }
        if (PropertyTypeExtMultiple.ID.equals(propertyTypeId)) {
            path = queryName + "." + "$[]" + "." + relationNameKey;
        } else {
            path = queryName + "." + relationNameKey;
        }
        return path;
    }

    public static String updateRelationValPath(String name) {
        String path = SearchPrefix + "." + name;
        return path;
    }

    public void importCheckInfo(MwImportResultParam resultParam, ImportContextParam contextParam, Map<String, Object> matchMap) {
        String message = "";
        List<ModelExportDataInfoParam> lists = contextParam.getListInfos();
        List<ModelExportDataInfoParam> list = new ArrayList<>(lists);
        List<PropertyInfo> modelPropertyList = contextParam.getModelPropertyList();
        Map<String, PropertyInfo> collect = new HashMap<>();
        if(modelPropertyList!=null){
            collect = modelPropertyList.stream().collect(Collectors.toMap(s -> s.getId(), s -> s, (
                    value1, value2) -> {
                return value2.getPropertySource() == null ? value2 : value1;
            }));
        }

        ModelExportDataInfoParam importParam = contextParam.getParam();
        //属性名称
        String tableName = importParam.getTableName();
        //是否唯一
        Boolean isOnly = importParam.getIsOnly();
        String propertyId = importParam.getPropertyId();
        //是否必填校验
        if (importParam.isMustCheck()) {
            message += tableName + "不能为空;";
            resultParam.setMessage(message);
        }
        Object cellValue = importParam.getCellValue();
        //唯一校验，针对导入数据的集合，进行唯一性判断(非空字段)
        if (booleanValueConvert(isOnly) && StringUtils.hasText(strValueConvert(cellValue).trim())) {
//            long count = list.stream().filter(s -> s.getPropertyId().equals(propertyId) && s.getCellValue() != null && cellValue != null
//                    && (strValueConvert(cellValue + ",").contains(strValueConvert(s.getCellValue() + ",")) || strValueConvert(s.getCellValue() + ",").contains(strValueConvert(cellValue + ",")))).count();
            //删除本条数据
            list.remove(importParam);
            List<ModelExportDataInfoParam> collect1 = list.stream().filter(s -> s.getPropertyId().equals(propertyId) && s.getCellValue() != null && cellValue != null).collect(Collectors.toList());
            Set<Object> collect2 = collect1.stream().map(s -> strValueConvert("," + s.getCellValue()) + ",").collect(Collectors.toSet());
            List<String> listSplit = Arrays.asList(strValueConvert(cellValue).split(","));
            //加上','是为了区别  测试1和测试11之前的匹配包含关系，变成'测试1,'和'测试11,'
            if (CollectionUtils.isNotEmpty(listSplit)) {
                for (String str : listSplit) {
                    if (collect2 != null) {
                        for (Object subStr : collect2) {
                            if (strValueConvert(subStr).contains("," + str + ",")) {
                                message += "导入数据中'" + tableName + "':" + str + ",不能重复;";
                                resultParam.setMessage(message);
                            }
                        }
                    }
                }

            }
        }
        if (collect != null && collect.containsKey(propertyId)) {
            PropertyInfo propertyInfo = collect.get(propertyId);
            if (propertyInfo.getPropertyTypeRef() != null && propertyInfo.getPropertyTypeRef().getContext() != null) {
                Map<String, Object> context = propertyInfo.getPropertyTypeRef().getContext();
                if (context != null && context instanceof Map) {
                    matchMap.putAll(context);
                }
            }
        }
        resultParam.setMessage(message);
    }
}
